<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2010 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// $Id$

import('TagLib');

/**
 +------------------------------------------------------------------------------
 * CX标签库解析类
 +------------------------------------------------------------------------------
 * @category   Think
 * @package  Think
 * @subpackage  Template
 * @author    liu21st <liu21st@gmail.com>
 * @version   $Id$
 +------------------------------------------------------------------------------
 */
class TagLibCx extends TagLib
{//类定义开始

// 标签定义
protected $tags   =  array(
// 标签定义： attr 属性列表 close 是否闭合（0 或者1 默认1） alias 标签别名 level 嵌套层次
        'php'=>array(),
        'volist'=>array('attr'=>'name,id,offset,length,key,mod','level'=>3,'alias'=>'iterate'),
        'foreach' =>array('attr'=>'name,item,key','level'=>3),
        'include'=>array('attr'=>'file','close'=>0),
        'if'=>array('attr'=>'condition'),
        'elseif'=>array('attr'=>'condition','close'=>0),
        'else'=>array('attr'=>'','close'=>0),
        'switch'=>array('attr'=>'name','level'=>3),
        'case'=>array('attr'=>'value,break'),
        'default'=>array('attr'=>'','close'=>0),
        'compare'=>array('attr'=>'name,value,type','level'=>3,'alias'=>'eq,equal,notequal,neq,gt,lt,egt,elt,heq,nheq'),
        'range'=>array('attr'=>'name,value,type','level'=>3,'alias'=>'in,notin'),
        'empty'=>array('attr'=>'name','level'=>3),
        'notempty'=>array('attr'=>'name','level'=>3),
        'present'=>array('attr'=>'name','level'=>3),
        'notpresent'=>array('attr'=>'name','level'=>3),
        'defined'=>array('attr'=>'name','level'=>3),
        'notdefined'=>array('attr'=>'name','level'=>3),
        'layout' =>array('attr'=>'name,cache','close'=>0),
        'import'=>array('attr'=>'file,href,type,value,basepath','close'=>0,'alias'=>'load,css,js'),
        'assign'=>array('attr'=>'name,value','close'=>0),
        'define'=>array('attr'=>'name,value','close'=>0),
);

/**
 +----------------------------------------------------------
 * include标签解析
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _include($attr,$content)
{
	$tag    = $this->parseXmlAttr($attr,'include');
	$file   =   $tag['file'];
	$parseStr = $this->tpl->parseInclude($file);
	foreach ($tag as $key=>$val) {
		if ($key == 'file') {
			continue;
		}
		//echo $key;
		$parseStr = str_replace('['.$key.']',$val,$parseStr);
	}
	return $this->tpl->parse($parseStr);
}

/**
 +----------------------------------------------------------
 * php标签解析
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _php($attr,$content) {
	$parseStr = '<?php '.$content.' ?>';
	return $parseStr;
}

/**
 +----------------------------------------------------------
 * volist标签解析 循环输出数据集
 * 格式：
 * <volist name="userList" id="user" empty="" >
 * {user.username}
 * {user.email}
 * </volist>
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string|void
 +----------------------------------------------------------
 */
public function _volist($attr,$content)
{
	static $_iterateParseCache = array();
	//如果已经解析过，则直接返回变量值
	$cacheIterateId = md5($attr.$content);
	if(isset($_iterateParseCache[$cacheIterateId]))
	return $_iterateParseCache[$cacheIterateId];
	$tag      = $this->parseXmlAttr($attr,'volist');
	$name   = $tag['name'];
	$id        = $tag['id'];
	$empty  = isset($tag['empty'])?$tag['empty']:'';
	$key     =   !empty($tag['key'])?$tag['key']:'i';
	$mod    =   isset($tag['mod'])?$tag['mod']:'2';
	// 允许使用函数设定数据集 <volist name=":fun('arg')" id="vo">{$vo.name}</volist>
	$parseStr   =  '<?php ';
	if(0===strpos($name,':')) {
		$parseStr   .= '$_result='.substr($name,1).';';
		$name   = '$_result';
	}else{
		$name   = $this->autoBuildVar($name);
	}
	$parseStr  .=  'if(is_array('.$name.')): $'.$key.' = 0;';
	if(isset($tag['length']) && '' !=$tag['length'] ) {
		$parseStr  .= ' $__LIST__ = array_slice('.$name.','.$tag['offset'].','.$tag['length'].',true);';
	}elseif(isset($tag['offset'])  && '' !=$tag['offset']){
		$parseStr  .= ' $__LIST__ = array_slice('.$name.','.$tag['offset'].',count($__LIST__),true);';
	}else{
		$parseStr .= ' $__LIST__ = '.$name.';';
	}
	$parseStr .= 'if( count($__LIST__)==0 ) : echo "'.$empty.'" ;';
	$parseStr .= 'else: ';
	$parseStr .= 'foreach($__LIST__ as $key=>$'.$id.'): ';
	$parseStr .= '++$'.$key.';';
	$parseStr .= '$mod = ($'.$key.' % '.$mod.' )?>';
	$parseStr .= $this->tpl->parse($content);
	$parseStr .= '<?php endforeach; endif; else: echo "'.$empty.'" ;endif; ?>';
	$_iterateParseCache[$cacheIterateId] = $parseStr;

	if(!empty($parseStr)) {
		return $parseStr;
	}
	return ;
}

public function _iterate($attr,$content) {
	return $this->_volist($attr,$content);
}

public function _foreach($attr,$content)
{
	static $_iterateParseCache = array();
	//如果已经解析过，则直接返回变量值
	$cacheIterateId = md5($attr.$content);
	if(isset($_iterateParseCache[$cacheIterateId]))
	return $_iterateParseCache[$cacheIterateId];
	$tag   = $this->parseXmlAttr($attr,'foreach');
	$name= $tag['name'];
	$item  = $tag['item'];
	$key   =   !empty($tag['key'])?$tag['key']:'key';
	$name= $this->autoBuildVar($name);
	$parseStr  =  '<?php if(is_array('.$name.')): foreach('.$name.' as $'.$key.'=>$'.$item.'): ?>';
	$parseStr .= $this->tpl->parse($content);
	$parseStr .= '<?php endforeach; endif; ?>';
	$_iterateParseCache[$cacheIterateId] = $parseStr;
	if(!empty($parseStr)) {
		return $parseStr;
	}
	return ;
}

/**
 +----------------------------------------------------------
 * if标签解析
 * 格式：
 * <if condition=" $a eq 1" >
 * <elseif condition="$a eq 2" />
 * <else />
 * </if>
 * 表达式支持 eq neq gt egt lt elt == > >= < <= or and || &&
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _if($attr,$content) {
	$tag          = $this->parseXmlAttr($attr,'if');
	$condition   = $this->parseCondition($tag['condition']);
	$parseStr  = '<?php if('.$condition.'): ?>'.$content.'<?php endif; ?>';
	return $parseStr;
}

/**
 +----------------------------------------------------------
 * else标签解析
 * 格式：见if标签
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _elseif($attr,$content) {
	$tag          = $this->parseXmlAttr($attr,'elseif');
	$condition   = $this->parseCondition($tag['condition']);
	$parseStr   = '<?php elseif('.$condition.'): ?>';
	return $parseStr;
}

/**
 +----------------------------------------------------------
 * else标签解析
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _else($attr) {
	$parseStr = '<?php else: ?>';
	return $parseStr;
}

/**
 +----------------------------------------------------------
 * switch标签解析
 * 格式：
 * <switch name="a.name" >
 * <case value="1" break="false">1</case>
 * <case value="2" >2</case>
 * <default />other
 * </switch>
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _switch($attr,$content) {
	$tag = $this->parseXmlAttr($attr,'switch');
	$name = $tag['name'];
	$varArray = explode('|',$name);
	$name   =   array_shift($varArray);
	$name = $this->autoBuildVar($name);
	if(count($varArray)>0)
	$name = $this->tpl->parseVarFunction($name,$varArray);
	$parseStr = '<?php switch('.$name.'): ?>'.$content.'<?php endswitch;?>';
	return $parseStr;
}

/**
 +----------------------------------------------------------
 * case标签解析 需要配合switch才有效
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _case($attr,$content) {
	$tag = $this->parseXmlAttr($attr,'case');
	$value = $tag['value'];
	if('$' == substr($value,0,1)) {
		$varArray = explode('|',$value);
		$value	=	array_shift($varArray);
		$value  =  $this->autoBuildVar(substr($value,1));
		if(count($varArray)>0)
		$value = $this->tpl->parseVarFunction($value,$varArray);
		$value   =  'case '.$value.': ';
	}elseif(strpos($value,'|')){
		$values  =  explode('|',$value);
		$value   =  '';
		foreach ($values as $val){
			$value   .=  'case "'.addslashes($val).'": ';
		}
	}else{
		$value	=	'case "'.$value.'": ';
	}
	$parseStr = '<?php '.$value.' ?>'.$content;
	$isBreak  = isset($tag['break']) ? $tag['break'] : '';
	if('' ==$isBreak || $isBreak) {
		$parseStr .= '<?php break;?>';
	}
	return $parseStr;
}

/**
 +----------------------------------------------------------
 * default标签解析 需要配合switch才有效
 * 使用： <default />ddfdf
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _default($attr) {
	$parseStr = '<?php default: ?>';
	return $parseStr;
}

/**
 +----------------------------------------------------------
 * compare标签解析
 * 用于值的比较 支持 eq neq gt lt egt elt heq nheq 默认是eq
 * 格式： <compare name="" type="eq" value="" >content</compare>
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _compare($attr,$content,$type='eq')
{
	$tag      = $this->parseXmlAttr($attr,'compare');
	$name   = $tag['name'];
	$value   = $tag['value'];
	$type    =   isset($tag['type'])?$tag['type']:$type;
	$type    =   $this->parseCondition(' '.$type.' ');
	$varArray = explode('|',$name);
	$name   =   array_shift($varArray);
	$name = $this->autoBuildVar($name);
	if(count($varArray)>0)
	$name = $this->tpl->parseVarFunction($name,$varArray);
	if('$' == substr($value,0,1)) {
		$value  =  $this->autoBuildVar(substr($value,1));
	}else {
		$value  =   '"'.$value.'"';
	}
	$parseStr = '<?php if(('.$name.') '.$type.' '.$value.'): ?>'.$content.'<?php endif; ?>';
	return $parseStr;
}

public function _eq($attr,$content) {
	return $this->_compare($attr,$content,'eq');
}

public function _equal($attr,$content) {
	return $this->_compare($attr,$content,'eq');
}

public function _neq($attr,$content) {
	return $this->_compare($attr,$content,'neq');
}

public function _notequal($attr,$content) {
	return $this->_compare($attr,$content,'neq');
}

public function _gt($attr,$content) {
	return $this->_compare($attr,$content,'gt');
}

public function _lt($attr,$content) {
	return $this->_compare($attr,$content,'lt');
}

public function _egt($attr,$content) {
	return $this->_compare($attr,$content,'egt');
}

public function _elt($attr,$content) {
	return $this->_compare($attr,$content,'elt');
}

public function _heq($attr,$content) {
	return $this->_compare($attr,$content,'heq');
}

public function _nheq($attr,$content) {
	return $this->_compare($attr,$content,'nheq');
}

/**
 +----------------------------------------------------------
 * range标签解析
 * 如果某个变量存在于某个范围 则输出内容 type= in 表示在范围内 否则表示在范围外
 * 格式： <range name="var|function"  value="val" type='in|notin' >content</range>
 * example: <range name="a"  value="1,2,3" type='in' >content</range>
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 * @param string $type  比较类型
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _range($attr,$content,$type='in') {
	$tag      = $this->parseXmlAttr($attr,'range');
	$name   = $tag['name'];
	$value   = $tag['value'];
	$varArray = explode('|',$name);
	$name   =   array_shift($varArray);
	$name = $this->autoBuildVar($name);
	$type    =   isset($tag['type'])?$tag['type']:$type;
	$fun  =  ($type == 'in')? 'in_array'    :   '!in_array';
	if(count($varArray)>0)
	$name = $this->tpl->parseVarFunction($name,$varArray);
	if('$' == substr($value,0,1)) {
		$value  =  $this->autoBuildVar(substr($value,1));
		$parseStr = '<?php if('.$fun.'(('.$name.'), is_array('.$value.')?'.$value.':explode(\',\','.$value.'))): ?>'.$content.'<?php endif; ?>';
	}else{
		$value  =   '"'.$value.'"';
		$parseStr = '<?php if('.$fun.'(('.$name.'), explode(\',\','.$value.'))): ?>'.$content.'<?php endif; ?>';
	}
	return $parseStr;
}

// range标签的别名 用于in判断
public function _in($attr,$content) {
	return $this->_range($attr,$content,'in');
}

// range标签的别名 用于notin判断
public function _notin($attr,$content) {
	return $this->_range($attr,$content,'notin');
}

/**
 +----------------------------------------------------------
 * present标签解析
 * 如果某个变量已经设置 则输出内容
 * 格式： <present name="" >content</present>
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _present($attr,$content)
{
	$tag      = $this->parseXmlAttr($attr,'present');
	$name   = $tag['name'];
	$name   = $this->autoBuildVar($name);
	$parseStr  = '<?php if(isset('.$name.')): ?>'.$content.'<?php endif; ?>';
	return $parseStr;
}

/**
 +----------------------------------------------------------
 * notpresent标签解析
 * 如果某个变量没有设置，则输出内容
 * 格式： <notpresent name="" >content</notpresent>
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _notpresent($attr,$content)
{
	$tag      = $this->parseXmlAttr($attr,'notpresent');
	$name   = $tag['name'];
	$name   = $this->autoBuildVar($name);
	$parseStr  = '<?php if(!isset('.$name.')): ?>'.$content.'<?php endif; ?>';
	return $parseStr;
}

/**
 +----------------------------------------------------------
 * empty标签解析
 * 如果某个变量为empty 则输出内容
 * 格式： <empty name="" >content</empty>
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _empty($attr,$content)
{
	$tag      = $this->parseXmlAttr($attr,'empty');
	$name   = $tag['name'];
	$name   = $this->autoBuildVar($name);
	$parseStr  = '<?php if(empty('.$name.')): ?>'.$content.'<?php endif; ?>';
	return $parseStr;
}

public function _notempty($attr,$content)
{
	$tag      = $this->parseXmlAttr($attr,'notempty');
	$name   = $tag['name'];
	$name   = $this->autoBuildVar($name);
	$parseStr  = '<?php if(!empty('.$name.')): ?>'.$content.'<?php endif; ?>';
	return $parseStr;
}
/**
 * 判断是否已经定义了该常量
 * <defined name='TXT'>已定义</defined>
 * @param <type> $attr
 * @param <type> $content
 * @return string
 */
public function _defined($attr,$content)
{
	$tag        = $this->parseXmlAttr($attr,'defined');
	$name     = $tag['name'];
	$parseStr = '<?php if(defined("'.$name.'")): ?>'.$content.'<?php endif; ?>';
	return $parseStr;
}

public function _notdefined($attr,$content)
{
	$tag        = $this->parseXmlAttr($attr,'_notdefined');
	$name     = $tag['name'];
	$parseStr = '<?php if(!defined("'.$name.'")): ?>'.$content.'<?php endif; ?>';
	return $parseStr;
}

/**
 +----------------------------------------------------------
 * 布局标签解析
 * 格式： <layout name=""  cache="30" />
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _layout($attr,$content) {
	$tag      = $this->parseXmlAttr($attr,'layout');
	$name   =   $tag['name'];
	$cache   =   isset($tag['cache'])?$tag['cache']:0;
	$parseStr=   "<!-- layout::$name::$cache -->";
	return $parseStr;
}

/**
 +----------------------------------------------------------
 * import 标签解析 <import file="Js.Base" /> <import file="Css.Base" type="css" />
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 * @param boolean $isFile  是否文件方式
 * @param string $type  类型
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _import($attr,$content,$isFile=false,$type='')
{
	$tag  = $this->parseXmlAttr($attr,'import');
	$file   = isset($tag['file'])?$tag['file']:$tag['href'];
	$parseStr = '';
	$endStr   = '';
	// 判断是否存在加载条件 允许使用函数判断(默认为isset)
	if (isset($tag['value']))
	{
		$varArray  = explode('|',$tag['value']);
		$name      = array_shift($varArray);
		$name      = $this->autoBuildVar($name);
		if (!empty($varArray))
		$name  = $this->tpl->parseVarFunction($name,$varArray);
		else
		$name  = 'isset('.$name.')';
		$parseStr .= '<?php if('.$name.'): ?>';
		$endStr    = '<?php endif; ?>';
	}
	if($isFile) {
		// 根据文件名后缀自动识别
		$type = $type?$type:(!empty($tag['type'])?strtolower($tag['type']):null);
		// 文件方式导入
		$array =  explode(',',$file);
		foreach ($array as $val){
			if (!$type || isset($reset))
			{
				$type = $reset = strtolower(substr(strrchr($val, '.'),1));
			}
			switch($type) {
				case 'js':
					$parseStr .= '<script type="text/javascript" src="'.$val.'"></script>';
					break;
				case 'css':
					$parseStr .= '<link rel="stylesheet" type="text/css" href="'.$val.'" />';
					break;
				case 'php':
					$parseStr .= '<?php require_cache("'.$val.'"); ?>';
					break;
			}
		}
	}else{
		// 命名空间导入模式 默认是js
		$type       = $type?$type:(!empty($tag['type'])?strtolower($tag['type']):'js');
		$basepath   = !empty($tag['basepath'])?$tag['basepath']:WEB_PUBLIC_PATH;
		// 命名空间方式导入外部文件
		$array =  explode(',',$file);
		foreach ($array as $val){
			switch($type) {
				case 'js':
					$parseStr .= '<script type="text/javascript" src="'.$basepath.'/'.str_replace(array('.','#'), array('/','.'),$val).'.js"></script>';
					break;
				case 'css':
					$parseStr .= '<link rel="stylesheet" type="text/css" href="'.$basepath.'/'.str_replace(array('.','#'), array('/','.'),$val).'.css" />';
					break;
				case 'php':
					$parseStr .= '<?php import("'.$val.'"); ?>';
					break;
			}
		}
	}
	return $parseStr.$endStr;
}

// import别名 采用文件方式加载(要使用命名空间必须用import) 例如 <load file="__PUBLIC__/Js/Base.js" />
public function _load($attr,$content)
{
	return $this->_import($attr,$content,true);
}

// import别名使用 导入css文件 <css file="__PUBLIC__/Css/Base.css" />
public function _css($attr,$content)
{
	return $this->_import($attr,$content,true,'css');
}

// import别名使用 导入js文件 <js file="__PUBLIC__/Js/Base.js" />
public function _js($attr,$content)
{
	return $this->_import($attr,$content,true,'js');
}

/**
 +----------------------------------------------------------
 * assign标签解析
 * 在模板中给某个变量赋值 支持变量赋值
 * 格式： <assign name="" value="" />
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _assign($attr,$content)
{
	$tag      = $this->parseXmlAttr($attr,'assign');
	$name   = $this->autoBuildVar($tag['name']);
	if('$'==substr($tag['value'],0,1)) {
		$value   =  $this->autoBuildVar(substr($tag['value'],1));
	}else{
		$value   =   '\''.$tag['value']. '\'';
	}
	$parseStr  = '<?php '.$name.' = '.$value.'; ?>';
	return $parseStr;
}

/**
 +----------------------------------------------------------
 * define标签解析
 * 在模板中定义常量 支持变量赋值
 * 格式： <define name="" value="" />
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param string $attr 标签属性
 * @param string $content  标签内容
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
public function _define($attr,$content)
{
	$tag      = $this->parseXmlAttr($attr,'define');
	$name   =  '\''.$tag['name']. '\'';
	if('$'==substr($tag['value'],0,1)) {
		$value   =  $this->autoBuildVar(substr($tag['value'],1));
	}else{
		$value   =   '\''.$tag['value']. '\'';
	}
	$parseStr  = '<?php define('.$name.', '.$value.'); ?>';
	return $parseStr;
}

}//类定义结束
?>